package fun.qu_an.minecraft.mini_games_helper.plovoice;

import com.google.common.collect.Maps;
import com.google.inject.Inject;
import lombok.Getter;
import net.minecraft.scoreboard.Team;
import net.minecraft.server.network.ServerPlayerEntity;
import org.jetbrains.annotations.NotNull;
import su.plo.lib.api.server.event.player.PlayerJoinEvent;
import su.plo.lib.api.server.event.player.PlayerQuitEvent;
import su.plo.lib.api.server.permission.PermissionDefault;
import su.plo.lib.api.server.player.MinecraftServerPlayer;
import su.plo.voice.api.addon.AddonInitializer;
import su.plo.voice.api.addon.AddonLoaderScope;
import su.plo.voice.api.addon.annotation.Addon;
import su.plo.voice.api.event.EventSubscribe;
import su.plo.voice.api.server.PlasmoVoiceServer;
import su.plo.voice.api.server.audio.capture.SelfActivationInfo;
import su.plo.voice.api.server.audio.capture.ServerActivation;
import su.plo.voice.api.server.audio.line.ServerSourceLine;
import su.plo.voice.api.server.audio.source.ServerDirectSource;
import su.plo.voice.api.server.player.VoicePlayer;
import su.plo.voice.proto.data.audio.capture.VoiceActivation;
import su.plo.voice.proto.data.audio.codec.opus.OpusDecoderInfo;
import su.plo.voice.proto.packets.tcp.clientbound.SourceAudioEndPacket;
import su.plo.voice.proto.packets.tcp.serverbound.PlayerAudioEndPacket;
import su.plo.voice.proto.packets.udp.clientbound.SourceAudioPacket;
import su.plo.voice.proto.packets.udp.serverbound.PlayerAudioPacket;

import java.util.Map;
import java.util.Objects;
import java.util.UUID;

@Addon(
	// An addon id must start with a lowercase letter and may contain only lowercase letters, digits, hyphens, and underscores.
	// It should be between 4 and 32 characters long.
	id = "qu-an-pv-addon-team", name = "Team Voice", scope = AddonLoaderScope.SERVER, version = "1.0.0", authors = {"Harvey_Husky"})
public class TeamVoice implements AddonInitializer {
	@Inject
	@Getter
	private PlasmoVoiceServer voiceServer;
	protected final Map<UUID, ServerDirectSource> sourceByPlayerId = Maps.newConcurrentMap();
	private ServerActivation teamActivation;
	private ServerSourceLine teamSourceLine;
	private SelfActivationInfo selfActivationInfo;

	@Override
	public void onAddonInitialize() {
		ServerSourceLine line = voiceServer.getSourceLineManager().getLineByName(VoiceActivation.PROXIMITY_NAME).get();
		this.teamSourceLine = voiceServer.getSourceLineManager().createBuilder(this, "Team", "qu_an.pv.activation.team",
				"qu_an_team:textures/icons/qu_an_team_voice.png", 12)
			.build();
		ServerActivation teamActivation = voiceServer.getActivationManager().createBuilder(this, "Team", "qu_an.pv.activation.team",
				"qu_an_team:textures/icons/qu_an_team_voice.png", "qu_an.pv.activation.team", 12)
			.setTransitive(true)
			.setProximity(false)
			.setPermissionDefault(PermissionDefault.TRUE)
			.setStereoSupported(true)
			.build();

		teamActivation.onPlayerActivation(this::onActivation);
		teamActivation.onPlayerActivationEnd(this::onActivationEnd);
		this.teamActivation = teamActivation;
		this.selfActivationInfo = new SelfActivationInfo(voiceServer.getUdpConnectionManager());
	}

	@EventSubscribe
	private void onPlayerJoin(PlayerJoinEvent ignoredEvent, MinecraftServerPlayer player) {
		voiceServer.getSourceLineManager().getLineById(VoiceActivation.PROXIMITY_ID).ifPresent(l -> l.removeSource(player.getUUID()));
	}

	@EventSubscribe
	private void onPlayerQuit(PlayerQuitEvent ignoredEvent, MinecraftServerPlayer player) {
		sourceByPlayerId.remove(player.getUUID());
	}

	private ServerActivation.Result onActivationEnd(@NotNull VoicePlayer voicePlayer, @NotNull PlayerAudioEndPacket packet) {
		ServerDirectSource source = sourceByPlayerId.get(voicePlayer.getInstance().getUUID());
		if (source == null) {
			return ServerActivation.Result.IGNORED;
		}
		SourceAudioEndPacket sourcePacket = new SourceAudioEndPacket(source.getId(), packet.getSequenceNumber());
		if (source.sendPacket(sourcePacket)) {
			return ServerActivation.Result.HANDLED;
		}
		return ServerActivation.Result.IGNORED;
	}

	private ServerActivation.Result onActivation(@NotNull VoicePlayer voicePlayer, @NotNull PlayerAudioPacket packet) {
		ServerPlayerEntity player = voicePlayer.getInstance().getInstance();
		Team team1 = player.getScoreboardTeam();

		ServerDirectSource source = sourceByPlayerId.compute(player.getUuid(), (k, v) -> {
			if (v != null) {
				v.setStereo(packet.isStereo() && teamActivation.isStereoSupported());
				return v;
			}
			ServerDirectSource source1 = teamSourceLine.createDirectSource(false, new OpusDecoderInfo());
			source1.setStereo(packet.isStereo() && teamActivation.isStereoSupported());
			source1.clearFilters();
			source1.addFilter((targetPlayer)
				-> !targetPlayer.equals(voicePlayer)
				   && Objects.equals(((ServerPlayerEntity) targetPlayer.getInstance().getInstance()).getScoreboardTeam(), team1));
			source1.setSender(voicePlayer);
			return source1;
		});

		SourceAudioPacket sourcePacket = new SourceAudioPacket(
			packet.getSequenceNumber(),
			(byte) source.getState(),
			packet.getData(),
			source.getId(),
			(short) 0
		);

		if (source.sendAudioPacket(sourcePacket, packet.getActivationId())) {
			selfActivationInfo.sendAudioInfo(voicePlayer, source, packet.getActivationId(), sourcePacket);
		}
		return ServerActivation.Result.HANDLED;
	}
}
